/* TDD by bhanlon 2/2005  http://toolbox/sites/23791/default.aspx */
/* Updated by eholtz 2/2005 to add per test messages and expose the test table */
#pragma once
#include <iostream>

namespace TDD 
{
	struct UnitTestInfo // a container to hold some info about each test
	{
		UnitTestInfo(const char* g, const char* t) : group(g), testname(t), status(true) {}
		UnitTestInfo(const UnitTestInfo* uti) : group(uti->group), testname(uti->testname), status(uti->status) {}
		const char *group, *testname;
		bool status;
	};

	struct TestVerification  // a container to hold some info about a test verification
		: public UnitTestInfo
	{
		TestVerification(UnitTestInfo* uti, unsigned long line, const char* fname, const char* vname) 
			: UnitTestInfo(uti), line_number(line), file_name(fname), name_string(vname), error_string(vname) 
		{

		}

		const char *file_name, *name_string, *error_string;
		unsigned long line_number;
	};

	typedef TestVerification TestFailure;

	struct TestMessage
		: public UnitTestInfo // a container to hold some message info 
	{
		TestMessage(const UnitTestInfo* uti, const char* message) : UnitTestInfo(uti), message_string(message) {}
		const char *message_string;
	};

	struct Reporter
	{                
		virtual void ForEachTestStart (const UnitTestInfo& uti) {}  // called once for each test
		virtual void ForEachTestEnd   (const UnitTestInfo& uti) {}  // called once for each test
		virtual void ForEachFailure(const TestVerification& tr) = 0;  // called once for each verification failure
		virtual void ForEachMessage(const TestMessage& tm) {}    // called once for each message
		virtual void ForEachSuccess(const TestVerification& tr) {}    // called once for each verification success
	};

	struct Discriminator
	{
		virtual bool WantTest(const UnitTestInfo& uti) const { return true; } // return true if you want to run this test
	};

	class TddException
	{
		unsigned long _line;
		const char * _file;
		const char * _msg;
	public:
		TddException(unsigned long line, const char * file, const char * msg) : _line(line), _file(file), _msg(msg) {}
		unsigned long GetLine() { return _line; }
		const char *  GetFile() { return _file; }
		virtual const char * GetExceptionText() { return _msg; }
	};

	class UnitTestBase : public UnitTestInfo
	{
	public:
		virtual void RunTest(Reporter& func) = 0;
		static void RunTests (/*in*/const Discriminator& pred, Reporter& func)
		{
			UnitTestBase* p = test_table;

			while (NULL != p) 
			{
				if (pred.WantTest(*p)) 
				{
					func.ForEachTestStart(*p);

					try 
					{
						p->RunTest(func);
					} 
					catch (TddException& e) 
					{
						func.ForEachFailure (TDD::TestFailure (p, e.GetLine(), e.GetFile(), e.GetExceptionText()));
					} 
					catch (...) 
					{
						func.ForEachFailure (TDD::TestFailure (p, __LINE__, __FILE__, "unknown exception:  continuing anyway"));
					}

					func.ForEachTestEnd(*p);
				}

				p = p->next;
			}
		}

	protected:
		UnitTestBase(char *g, char *t) 
			: UnitTestInfo(g, t), next(0) 
		{ 
			AddTest(this); 
		}

	private:
		UnitTestBase *next;
		static UnitTestBase *test_table;

		static void AddTest(UnitTestBase *t)
		{
			UnitTestBase * p = test_table;

			if (NULL == p)   // empty: add to beginning
			{
				test_table = t;
			}
			else 
			{
				while (NULL != p->next) 
				{
					p = p->next;
				}

				p->next = t;   // add to end
			}
		}		

	public:
		static const UnitTestBase *GetTests() 
		{
			return test_table;
		}

		const UnitTestBase *GetNext() const
		{
			return next;
		}
	};

#define TEST(group, test) \
	struct group##test : public TDD::UnitTestBase {\
	group##test() : UnitTestBase(#group, #test) {}\
	virtual void RunTest(TDD::Reporter& functor);\
	};\
	group##test group##test##_var;\
	void group##test::RunTest(TDD::Reporter& functor)


	inline void Verify (TDD::Reporter& func, TDD::UnitTestInfo * test, int line, char * filename, bool b, const char * VerificationName)
	{
		test->status = b;

		if (!b)
		{
			func.ForEachFailure (TDD::TestVerification (test, line, filename, VerificationName));
		}
		else
		{
			func.ForEachSuccess (TDD::TestVerification (test, line, filename, VerificationName));
		}
	}

	inline void Verify (TDD::Reporter& func, TDD::UnitTestInfo * test, int line, char * filename, int /* BOOL */ b,   const char * VerificationName)
	{ 
		Verify (func, test, line, filename, !!b, VerificationName);
	}

	inline void Message(TDD::Reporter& func, TDD::UnitTestInfo * test, const char* messageString)
	{
		func.ForEachMessage(TDD::TestMessage(test, messageString));
	}


#define TDD_VERIFY(testname, arg)        TDD::Verify(functor, this, __LINE__, __FILE__, arg, testname)
#define TDD_VERIFY_HRESULT(testname, hr) TDD::Verify(functor, this, __LINE__, __FILE__, (hr) >= 0, testname)
#define TDD_FAILURE(errorString)         TDD::Verify(functor, this, __LINE__, __FILE__, false, errorString)
#define TDD_MESSAGE(messageString)       TDD::Message(functor, this, messageString)
#define THROW_TDD_EXCEPTION(msg)         throw TDD::TddException(__LINE__, __FILE__,msg)

}  // namespace TDD
